Generally, computer applications run by executing code. The code controls the actions of the computer systems on which it is run. In order to protect the application from unauthorized tampering with the code, a number of security precautions may be utilized.
Some of these security measures are physical. For example, a user purchasing software on a CD-ROM may be able to verify that the CD-ROM is a legitimate copy of the software by inspecting holograms or other security devices on the packaging.
Module authentication is a security measure that executes on a computer system and attempts to ensure the security of software applications by providing a level of protection against tampering.
Static module authentication is a mechanism to detect tampering of the portable executable binary modules that comprise a software application. These binary modules are stored on the persistent storage device, which is usually a file on a hard disk and therefore often referred to as the “on-disk” image of the module. Authentication of the on-disk image may be accomplished by cryptographically hashing the file and comparing the result to a precomputed hash of the file. A cryptographic hashing algorithm operates on binary data of arbitrary size and generates a fixed size value known as a hash. Cryptographic hashes are computed such that any modification of the hashed data will cause a change in the generated hash. A hash is computed of the on-disk module. It is almost completely impossible to make a modification of a file to insert malicious changes and yet maintain the same hash of the modified file. Therefore, the hash of a file can be compared to a stored hash in order to verify that no modifications have been made. In order to prevent an adversary from changing the stored precomputed hash as well as the module being validated, the validity of the stored hash must be verifiable. For example, the stored hash may be cryptographically signed by a trusted signatory.
However, in order to be executed, a file constituting the portable executable (PE) computer program module (e.g. a DLL or EXE) must be loaded into memory. Once loaded, this “in-memory” module image is subject to potentially malicious modifications such as code patching, detouring and software debugger breakpoints. Consequently, it is necessary to authenticate the in-memory image of the module as well as the on-disk image. Authentication of this in-memory executable image of the file is known as “in-memory authentication” or “dynamic module authentication”.
Since tampering of the in-memory images of application modules can occur at any time during the life of the application session, it is usually desirable from a security standpoint to repeat the in-memory authentication process multiple times.
There are two significant reasons why computing a file hash is not an efficient basis for repeated verification of the in-memory module image. First, working set optimizations may be performed by the operating system or by the program itself which cause not all of an executable to be loaded into memory at any given time. This is done to reduce the memory footprint of the running process, for example, so that many processes can run on a computer with limited memory and not cause page swapping. It also minimizes the use of memory bus and device I/O bandwidth (for example, to the hard disk where the executable image is stored) consumed by subsequent page swapping. Memory and bandwidth are valuable fixed resources dictated by the hardware configuration of the computer, so a reduction in the use of either improves the behavior of the computer on all running applications. However, each time a whole file hash must be computed, the entire file must be loaded into physical memory. This negates the benefit of any working set optimizations performed by the operating system or the program itself because the entire module image must be loaded from disk into memory.
A second reason is that a module's in-memory image is not identical to its on-disk file image and the differences must be accounted for when comparing to a file hash. For example, address “relocations” may be applied to the in-memory image by the operating system. That is, the PE file is stored on-disk with a predefined preferred base address where the operating system (OS) loader will attempt to load the file in memory. The code in the on-disk image will be aligned with that base address. However, if there is an address collision with a module that has already been loaded at the preferred address, the OS must relocate, or “rebase”, the module to a new location. When this rebasing does occur, all addresses which assumed the original preferred address within the module image need to change to reflect the base address where the PE file is actually loaded. The PE file contains relocation data which describes where these relocations must be applied. The OS loader applies the relocations to the addresses specified in the relocation data which is a data section in the on-disk PE image. Additionally, the in-memory copy of the import address table (IAT) within the loaded PE file will be modified by the OS loader as function addresses are resolved against DLL export tables. The IAT describes the in memory-address locations of functions in other PEmodules. When the PE is loaded by the OS, the IAT is changed from the original on-disk version of the PE to reflect the actual locations of functions that can be called in other PE modules outside of the PE.
There are multiple methods for performing an in-memory authentication based on a file hash. Each suffers from the inefficiencies mentioned previously. One method is to copy the entire on-disk image into memory to compute its hash, compare the hash to the stored signed hash, apply loader changes to the copy and finally compare the copy to the in-memory image which was loaded by the OS. Another method would be to back out loader changes from the in-memory image into another portion of memory, hash that memory and then compare the hash to the signed file hash. Both methods involve three basic tasks: 1) the in-memory image must be swapped into physical memory, 2) a copy of the image must be made in memory and 3) the expected differences between the in-memory image and the on-disk image caused by the operating system loader must be accounted for. These basic tasks are extremely inefficient, in terms of computation, I/O bandwidth consumption, transfer time, and memory allocation.
In order to reduce space and time requirements for loading, many current executables are created in such a way so as to obviate the need for the entire executable to be loaded by the OS in order for the program's functionality to be used. Some portion of the executable is loaded for the initial functionality, and additional portions are loaded only if and when they become necessary. For example, in one scenario, a program with a help system is executed. When the program is initially run, unneeded help system portions of the executable are not loaded. Thus, starting up the program is not delayed.
If, as described, some portions are not immediately loaded at startup, these portions may be loaded at a later time, either upon opportunity (when the loading will not cause noticeable user delay) or when additional functionality is needed. As in the example, the help system may be loaded when the user first requests use of the help system. This will reduce unnecessary use of memory in loading the help system before it is needed. However, there may be a noticeable delay to the user. Thus, alternatively, the help system may be loaded before the help system has been requested, but after the program has been started up for the user, during a time when such loading will not adversely affect the user experience unduly. In this way, while memory is used for the help system portions in advance of their use, there will be minimal delay when the user requests the help system.
However, as described, above, in order to perform in-memory authentication according to the prior art, the entire on-disk image of the PE is loaded into memory. Thus, either the advantages of delayed loading of portions of the executable can not be realized or the security of an in-memory authentication is not achieved.
In view of the foregoing, there is a need for a system that overcomes the drawbacks of the prior art.